/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ua.lviv.franko.matrix.hash.gauss;

import java.util.List;

import ua.lviv.franko.matrix.IMatrix;

/**
 * 
 * @author marko
 */
public class Gauss {
	// матрица
	private IMatrix	matrix;

	// конструктор, принимает созданную матрицу коэффициентов
	public Gauss(IMatrix matrix) {
		this.matrix = matrix;
	}

	// главный метод, возвращающий решение, принимает вектор свободных членов
	public double[] calculate(double[] B) {
		// обнуляем нижнюю полуматрицу, перебирая сверху вниз все строки
		// и складывая каждую со всеми нижележащими
		for (int row = 0; row < (matrix.getN() - 1); row++) {
			// получаем все ненулевые значения обнуляемого столбца
			List<Integer> colIndexes = matrix.getJColIndex(row);
			List<Double> colValues = matrix.getJColValues(row);

			// получаем индексы и значения ячеек строки, правее главной диагонали
			// ячейки левее главной диагонали к этому моменту уже обнулены
			List<Integer> rowIndexes = matrix.getJRowIndex(row);
			List<Double> rowValues = matrix.getJRowValues(row);
			// получаем элемент главной диагонали, которым будем обнулять столбец
			double dd = matrix.getValue(row, row);
			for (int i = 0; i < colIndexes.size(); i++) {
				// высчитываем коэффициент
				double k = colValues.get(i) / dd;
				// k подобран таким образом чтобы обнулить ячейку столбца,
				matrix.setValue(colIndexes.get(i), row, 0);
				// складываем строки
				for (int ii = 0; ii < rowIndexes.size(); ii++) {
					matrix.addValue(colIndexes.get(i), rowIndexes.get(ii), -rowValues.get(ii) * k);
				}
				// складываем соответствующие свободные члены
				B[colIndexes.get(i)] -= B[row] * k;
			}
		}
		// вектор невідомих
		double[] x = new double[matrix.getN()];
		// обернеий хід

		for (int row = (matrix.getN() - 1); row >= 0; row--) {
			double e = 0;
			List<Integer> indexes = matrix.getJRowIndex(row);
			List<Double> values = matrix.getJRowValues(row);
			for (int i = 0; i < indexes.size(); i++)
				e += x[indexes.get(i)] * values.get(i);
			x[row] = (B[row] - e) / matrix.getValue(row, row);
		}
		return x;
	}

	public static double[][] calculate(double[][] a, double[][] b) {
		int n = a.length;
		for (int i = 0; i < n; i++) {
			int best = i;
			for (int j = i + 1; j < n; j++) {
				if (Math.abs(a[j][i]) > Math.abs(a[best][i])) {
					best = j;
				}
			}
			double[] tmp = a[i];
			a[i] = a[best];
			a[best] = tmp;

			tmp = b[i];
			b[i] = b[best];
			b[best] = tmp;

			for (int j = i + 1; j < n; j++) {
				a[i][j] /= a[i][i];
			}
			for (int j = 0; j < b[i].length; j++) {
				b[i][j] /= a[i][i];
			}

			for (int j = 0; j < n; j++) {
				if (j != i && a[j][i] != 0) {
					for (int p = i + 1; p < n; p++) {
						a[j][p] -= a[i][p] * a[j][i];
					}
					for (int k = 0; k < b[j].length; k++) {
						b[j][k] -= b[i][k] * a[j][i];
					}

				}
			}
		}
		return b;
	}

	public static double[] calculate(double[][] a, double[] b) {
		int n = a.length;
		for (int i = 0; i < n; i++) {
			int best = i;
			for (int j = i + 1; j < n; j++) {
				if (Math.abs(a[j][i]) > Math.abs(a[best][i])) {
					best = j;
				}
			}
			double[] tmp = a[i];
			a[i] = a[best];
			a[best] = tmp;

			double tmpVal = b[i];
			b[i] = b[best];
			b[best] = tmpVal;

			for (int j = i + 1; j < n; j++) {
				a[i][j] /= a[i][i];
			}

			b[i] /= a[i][i];

			for (int j = 0; j < n; j++) {
				if (j != i && a[j][i] != 0) {
					for (int p = i + 1; p < n; p++) {
						a[j][p] -= a[i][p] * a[j][i];
					}
					b[j] -= b[i] * a[j][i];

				}
			}
		}
		return b;
	}
}
